package me.edzh.spring.bootstrap.utils.security;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.Data;
import me.edzh.spring.bootstrap.model.IUser;
import me.edzh.spring.bootstrap.utils.Constants;
import org.apache.http.util.TextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class Jwt {
    public static class SecurityOptions {
        public String secret;
        public long expireTime;
    }
    public interface SecurityProvider{
        SecurityOptions getSecurityOptions();
        String getIssuer();
    }

    @Autowired(required = false)
    private SecurityProvider securityProvider;

    public String getToken(IUser user){
        if (securityProvider == null) {
            throw new RuntimeException("JWT is unavailable until Jwt.SecurityProvider is implemented");
        }

        SecurityOptions options = securityProvider.getSecurityOptions();
        try {
            return JWT.create()
                    .withIssuer(securityProvider.getIssuer())
                    .withClaim(Constants.JWT_KEY_USER_ID, user.getId())
                    .withClaim(Constants.JWT_KEY_USER_PRIVILEGES, user.getAuthPrivileges())
                    .withExpiresAt(new Date(System.currentTimeMillis() + options.expireTime))
                    .sign(Algorithm.HMAC256(options.secret));
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public IUser getUser(String token){
        if (securityProvider == null) {
            throw new RuntimeException("JWT is unavailable until Jwt.SecurityProvider is implemented");
        }

        if (TextUtils.isEmpty(token)){
            return null;
        }

        SecurityOptions options = securityProvider.getSecurityOptions();
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(options.secret)).withIssuer(securityProvider.getIssuer()).build();
            DecodedJWT jwt = verifier.verify(token);

            JWTUser user = new JWTUser();
            try {
                user.setId(jwt.getClaim(Constants.JWT_KEY_USER_ID).asString());
                user.setAuthPrivileges(jwt.getClaim(Constants.JWT_KEY_USER_PRIVILEGES).asInt());
            } catch (Throwable ignore) {
                return null;
            }

            return user;
        } catch (TokenExpiredException tokenExpiredException) {
            return null;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    @Data
    private static class JWTUser implements IUser{
        private String id;
        private int authPrivileges;
    }
}
